package com.mall.order.service.impl;

import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.common.constants.OrderConstants;
import com.common.utils.R;
import com.common.vo.MemberVo;
import com.mall.order.entity.OrderItemEntity;
import com.mall.order.exception.NoStackException;
import com.mall.order.feign.CartFeignService;
import com.mall.order.feign.MemberServiceFeign;
import com.mall.order.feign.ProductService;
import com.mall.order.feign.WareFeignService;
import com.mall.order.intrceptor.AuthInterceptor;
import com.mall.order.service.OrderItemService;
import com.mall.order.utils.OrderMsgProducer;
import com.mall.order.vo.*;
import com.mall.order.vo.dto.OrderCreateDto;
import com.mall.order.vo.result.LockStockResult;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.Collectors;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.common.utils.PageUtils;
import com.common.utils.Query;

import com.mall.order.dao.OrderDao;
import com.mall.order.entity.OrderEntity;
import com.mall.order.service.OrderService;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;


@Service("orderService")
public class OrderServiceImpl extends ServiceImpl<OrderDao, OrderEntity> implements OrderService {
    @Autowired
    private MemberServiceFeign memberServiceFeign;
    @Autowired
    private CartFeignService cartFeignService;
    @Autowired
    ThreadPoolExecutor threadPoolExecutor;
//    @Autowired
//    private StringRedisTemplate stringRedisTemplate;
    @Autowired
    private ProductService productService;
    @Autowired
    private OrderService orderService;
    @Autowired
    private OrderItemService orderItemService;
    @Autowired
    private WareFeignService wareFeignService;
    @Autowired
    private OrderMsgProducer orderMsgProducer;


    @Override
    public PageUtils queryPage(Map<String, Object> params) {
        IPage<OrderEntity> page = this.page(
                new Query<OrderEntity>().getPage(params),
                new QueryWrapper<OrderEntity>()
        );

        return new PageUtils(page);
    }

    @Override
    public OrderConfirmVo confirmOder() {
        OrderConfirmVo vo = new OrderConfirmVo();
        MemberVo memberVo = (MemberVo) AuthInterceptor.threadLocal.get();
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        CompletableFuture<Void> future1 = CompletableFuture.runAsync(() -> {
            RequestContextHolder.setRequestAttributes(requestAttributes);
            Long id = memberVo.getId();
            List<MemberAddressVo> addressVoList = memberServiceFeign.getAddress(id);
            vo.setAddress(addressVoList);
        }, threadPoolExecutor);
        CompletableFuture<Void> future2 = CompletableFuture.runAsync(() -> {
            RequestContextHolder.setRequestAttributes(requestAttributes);
            List<OrderItemVo> userCartItems = cartFeignService.getUserCartItems();
            vo.setItems(userCartItems);
        }, threadPoolExecutor);
        try {
            CompletableFuture.allOf(future1, future2).get();
        } catch (Exception e) {
            e.printStackTrace();
        }
        String token = UUID.randomUUID().toString().replace("-", "");
//        stringRedisTemplate.opsForValue().set(OrderConstants.ORDER_REDIS_KEY + ":" + memberVo.getId(), token);
        return vo;
    }

    private Lock lock = new ReentrantLock();

    @Override
    @Transactional
    public OrderResponseVo orderSubmit(OrderSubmitVo vo) throws NoStackException {
        MemberVo memberVo = (MemberVo) AuthInterceptor.threadLocal.get();
        String key = OrderConstants.ORDER_REDIS_KEY + ":" + memberVo.getId();
//        try {
//            lock.lock();
//            String redisToken = stringRedisTemplate.opsForValue().get(key);
        OrderResponseVo responseVo = new OrderResponseVo();
//            if (ObjectUtil.isNotNull(redisToken) && redisToken.equals(vo.getOrderToken())) {
//                stringRedisTemplate.delete(key);
//            }else {
//                return responseVo;
//            }
//        }finally {
//            lock.unlock();
        String script = "if redis.call('get',KEYS[1])==ARGV[1] then return redis.call('del',KEYS[1]) else return 0";
//        Long result = stringRedisTemplate.execute(new DefaultRedisScript<Long>(script, Long.class), Arrays.asList(key), vo.getOrderToken());
//        if (result == 0) {
//            responseVo.setCode(1);
//            return responseVo;
//        }
        OrderCreateDto createDto = createOrder(vo);
        responseVo.setOrderEntity(createDto.getOrderEntity());
        saveOrder(createDto);
        return lockWareSkuStock(responseVo, createDto);
    }

    /**
     * 锁定库存操作
     * @param responseVo
     * @param createDto
     * @return
     */
    private OrderResponseVo lockWareSkuStock(OrderResponseVo responseVo,OrderCreateDto createDto){
        WareSkuLockVo wareSkuLockVo = new WareSkuLockVo();
        wareSkuLockVo.setOrderSn(createDto.getOrderEntity().getOrderSn());
        List<OrderItemVo> orderItemVos = createDto.getOrderItemEntitys().stream().map(item -> {
            OrderItemVo itemVo = new OrderItemVo();
            itemVo.setSkuId(itemVo.getSkuId());
            itemVo.setTitle(item.getSkuName());
            itemVo.setCount(item.getSkuQuantity());
            return itemVo;
        }).collect(Collectors.toList());
        wareSkuLockVo.setItems(orderItemVos);
        R r  = wareFeignService.orderLockStock(wareSkuLockVo);
        if (r.getCode() == 0){
            //订单创建成功
            responseVo.setCode(0);
        }else {
            //锁定失败
            responseVo.setCode(2);
            throw new NoStackException("库存锁定失败");
        }
        //发送延迟30分钟的消息
        orderMsgProducer.sendOrderMessage(createDto.getOrderEntity().getOrderSn());
        return responseVo;
    }

    /**
     * 生成3订单数据
     * @param createDto
     */
    private void saveOrder(OrderCreateDto createDto) {
        OrderEntity orderEntity = createDto.getOrderEntity();
        orderService.save(orderEntity);
        List<OrderItemEntity> orderItemEntitys = createDto.getOrderItemEntitys();
        orderItemService.saveBatch(orderItemEntitys);

    }

    private OrderCreateDto createOrder(OrderSubmitVo vo) {
        OrderCreateDto dto = new OrderCreateDto();
        OrderEntity orderEntity = buildOrder(vo);
        dto.setOrderEntity(orderEntity);
        List<OrderItemEntity> orderItemList = buildOrderItems(orderEntity.getOrderSn());
        dto.setOrderItemEntitys(orderItemList);
        return dto;
    }

    /**
     * 创建对应的购物想信息
     * @return
     */
    private List<OrderItemEntity> buildOrderItems(String orderSn) {
        List<OrderItemEntity> orderItemList = new ArrayList<>();
        List<OrderItemVo> userCartItems = cartFeignService.getUserCartItems();
        if (ObjectUtil.isNotNull(userCartItems)) {
            List<Long> skuIdList = new ArrayList<>();
            for (OrderItemEntity orderItemEntity : orderItemList) {
                if (!skuIdList.contains(orderItemEntity.getSpuId())){
                    skuIdList.add(orderItemEntity.getOrderId());
                }
            }
            Long[] spuIds = new Long[skuIdList.size()];
            List<OrderItemSpuInfoVo> info = productService.getOrderItemSpuInfoBySpuId(skuIdList.toArray(spuIds));
            Map<Long, OrderItemSpuInfoVo> map = info.stream().collect(Collectors.toMap(OrderItemSpuInfoVo::getId, item -> item));

            userCartItems.forEach(item -> {
                OrderItemSpuInfoVo itemSpuInfoVo = map.get(item.getSpuId());
                OrderItemEntity orderItemEntity = buildOrderItem(item,itemSpuInfoVo);
                orderItemEntity.setOrderSn(orderSn);
                orderItemList.add(orderItemEntity);

            });
        }
        return orderItemList;
    }

    /**
     * 创建对应的订单项
     * @param vo
     * @return
     */
    private OrderItemEntity buildOrderItem(OrderItemVo vo,OrderItemSpuInfoVo info) {
        OrderItemEntity orderItemEntity = new OrderItemEntity();
        orderItemEntity.setSkuId(vo.getSkuId());
        orderItemEntity.setSkuName(vo.getTitle());
        orderItemEntity.setSkuPic(vo.getImage());
        orderItemEntity.setSkuQuantity(vo.getCount());
        List<String> skuAttr = vo.getSkuAttr();
        String skuAttrStr = StringUtils.collectionToDelimitedString(skuAttr, ";");
        orderItemEntity.setSkuAttrsVals(skuAttrStr);
        orderItemEntity.setSpuId(info.getId());
        orderItemEntity.setSpuBrand(info.getBrandName());
        orderItemEntity.setCategoryId(info.getCatalogId());
        orderItemEntity.setSpuPic(info.getImg());
        orderItemEntity.setGiftGrowth(vo.getPrice().intValue());
        orderItemEntity.setGiftIntegration(vo.getPrice().intValue());
        return orderItemEntity;
    }

    private OrderEntity  buildOrder(OrderSubmitVo vo){
        OrderEntity entity = new OrderEntity();
        String timeId = IdWorker.getTimeId();
        entity.setOrderSn(timeId);
        MemberVo memberVo  = (MemberVo) AuthInterceptor.threadLocal.get();
        entity.setMemberId(memberVo.getId());
        entity.setMemberUsername(memberVo.getUsername());
        MemberAddressVo addressVo = memberServiceFeign.info(vo.getAddrId());
        entity.setReceiverCity(addressVo.getDetailAddress());
        entity.setReceiverName(addressVo.getName());
        entity.setReceiverPostCode(addressVo.getPostCode());
        entity.setReceiverRegion(addressVo.getRegion());
        entity.setReceiverProvince(addressVo.getProvince());
        entity.setReceiverDetailAddress(addressVo.getDetailAddress());
        entity.setReceiverPhone(addressVo.getPhone());
        return entity;
    }
}